using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using igp.net.DataModel;

namespace igp.net
{
    /// <summary>
    /// Represents the main form of the program.
    /// </summary>
    public partial class FormMain : Form
    {
        private readonly List<String> log;

        private FormLog formLog;

        /// <summary>
        /// Gets the reference to the picker window.
        /// </summary>
        public FormPicker PickerWindow { get; private set; }

        /// <summary>
        /// Gets the reference to the about window.
        /// </summary>
        public FormAbout AboutWindow { get; private set; }

        /// <summary>
        /// Gets the reference to the editor window.
        /// </summary>
        public FormEditor EditorWindow { get; private set; }

        /// <summary>
        /// Gets the reference to the options window.
        /// </summary>
        public FormOptions OptionsWindow { get; private set; }

        private readonly IgpProject project = IgpProject.createProject();

        private int DrawAtX;

        private int DrawAtY;

        /// <summary>
        /// Gives access to the recent documents list.
        /// </summary>
        public List<string> RecentDocuments { get; private set; }

        /// <summary>
        /// Creates a singleton of this class.
        /// If an instance already exists then an exception is thrown.
        /// </summary>
        /// <returns>Created form</returns>
        public static FormMain createForm()
        {
            if (Singleton != null)
            {
                throw new Exception("An instance already exists.");
            }
            Singleton = new FormMain();
            Singleton.loadSettings();
            Singleton.PrepareForms();

            if (igpSettings.Singleton.ShowEditor)
            {
                Singleton.EditorWindow.Show();
            }

            if (igpSettings.Singleton.ShowPicker)
            {
                Singleton.PickerWindow.Show();
            }

            Singleton.RestoreWindowsLocations();

            Singleton.RefreshDocumentsList();
            return Singleton;
        }

        private void RestoreWindowsLocations()
        {
            EditorWindow.Left = igpSettings.Singleton.EditorWindowLeft;
            EditorWindow.Top = igpSettings.Singleton.EditorWindowTop;
            EditorWindow.Height = igpSettings.Singleton.EditorWindowHeight;

            PickerWindow.Left = igpSettings.Singleton.PickerWindowLeft;
            PickerWindow.Top = igpSettings.Singleton.PickerWindowTop;
            PickerWindow.Width = igpSettings.Singleton.PickerWindowWidth;
            PickerWindow.Height = igpSettings.Singleton.PickerWindowHeight;
        }

        private void PrepareForms()
        {
            formLog = new FormLog();
            PickerWindow = new FormPicker();
            EditorWindow = new FormEditor();
            OptionsWindow = new FormOptions();
            AboutWindow = new FormAbout();

            formLog.Owner = this;
            PickerWindow.Owner = this;
            EditorWindow.Owner = this;
            OptionsWindow.Owner = this;
            AboutWindow.Owner = this;
        }

        private void loadSettings()
        {
            if (!System.IO.File.Exists("igp.ini"))
            {
                LogMessage("Configuration data not found. Using defauls.");

            } else
            {
                project.LoadSettings("igp.ini");
                LogMessage("Settings loaded successfully.");
            }
        }

        private FormMain()
        {
            InitializeComponent();

             //Set culture to english
            Application.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(1033);

            RecentDocuments = new List<string>();
            log = new List<string>();
        }

        /// <summary>
        /// Gives access to the only instance of the main form.
        /// </summary>
        public static FormMain Singleton { get; private set; }

        /// <summary>
        /// Renders the project image in memory.
        /// </summary>
        public void Render()
        {
            //Render image in memory
            project.Draw();
        }

        /// <summary>
        /// Adds new item to the recent documents list.
        /// </summary>
        /// <param name="Argument"></param>
        public void AddRecentDocument(string Argument)
        {
            if (RecentDocuments.Count == 5 || RecentDocuments.Contains(Argument)) return;
            
            RecentDocuments.Add(Argument);
        }

        /// <summary>
        /// Refreshes Items on the recent documents list on the start page.
        /// </summary>
        public void RefreshDocumentsList()
        {
            toolStripMenuItemRecentDocuments.DropDown = contextMenuStripRecentDocs;
            contextMenuStripRecentDocs.Items.Clear();
            
            if (RecentDocuments.Count == 0)
            {
                toolStripMenuItemRecentDocuments.Enabled = false;
                return;
            }

            toolStripMenuItemRecentDocuments.Enabled = true;

            foreach (string a in RecentDocuments)
            {
                contextMenuStripRecentDocs.Items.Add(a);
            }
        }

        private void UpdateDocumentsList(string file)
        {
            if (RecentDocuments.Contains(file))
            {
                RecentDocuments.Remove(file);
                
            }

            RecentDocuments.Insert(0,file);
        }
        /// <summary>
        /// This function fits drawing area to the window dimensions
        /// </summary>
        public void FitViewToPanel()
        {
            IgpProject.Singleton.View.VWidth = panelView.Width - 4;
            IgpProject.Singleton.View.VHeight = panelView.Height - 4;

            IgpProject.Singleton.View.Width = panelView.Width - 4;
            IgpProject.Singleton.View.Height = panelView.Height - 4;

            IgpProject.Singleton.UpdateResolutions();

            EditorWindow.ReadViewProperities();
        }

        /// <summary>
        /// Redraws rendered image inside the window.
        /// </summary>
        public void PaintView()
        {
            //Set Left Corner of Destination Rectangle
            DrawAtX = panelView.Width / 2 - IgpProject.Singleton.View.VWidth / 2;
            DrawAtY = panelView.Height / 2 - IgpProject.Singleton.View.VHeight / 2;

            //Draw the image on screen
            Rectangle temp = new Rectangle(DrawAtX, DrawAtY, IgpProject.Singleton.View.VWidth, IgpProject.Singleton.View.VHeight);
            Rectangle temp2 = new Rectangle(DrawAtX - 1, DrawAtY - 1, IgpProject.Singleton.View.VWidth + 1, IgpProject.Singleton.View.VHeight + 1);

            Graphics Client = panelView.CreateGraphics();
            Client.DrawImage(project.Bitmap, temp);

            //Draw frame
            if (igpSettings.Singleton.ViewBorder) Client.DrawRectangle(new Pen(Color.DarkGray, 1), temp2);
        }

        /// <summary>
        /// Clears the view area of the main form.
        /// </summary>
        public void ClearView()
        {
            panelView.Refresh();
        }

        /// <summary>
        /// Sets the message in the statusbar.
        /// </summary>
        /// <param name="mes"></param>
        public void LogMessage(string mes)
        {
            toolStripStatusLabelMessage.Image = mes.IndexOf("error: ") != -1 ? Properties.Resources.error : Properties.Resources.information;
            toolStripStatusLabelMessage.Text = " " + mes;
            
            log.Insert(0,mes);
            
            if (formLog != null)
            {
                formLog.Update(log);
            }
        }

        /// <summary>
        /// Sets the project name that will be displayed in the window title.
        /// </summary>
        /// <param name="projectName">Project name</param>
        public void SetWindowTitle(string projectName)
        {
            Text = "igp.net [ " + projectName + " ]";
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Prepare data
            IgpProject.Singleton.clearProject();
            IgpProject.Singleton.UpdateResolutions();

            // Reset UI
            EditorWindow.SetActivePage(0);
            SetWindowTitle("New project");
            saveProjectDialog.FileName = "";
            openProjectDialog.FileName = "";
            EditorWindow.ReadViewProperities();
            EditorWindow.SetActivePage(0);
            PickerWindow.RefreshList();
            

            // Render the view
            ClearView();
            Render();
            PaintView();

            LogMessage("New project successfully created");
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openProjectDialog.ShowDialog() != DialogResult.OK) return;

            // Load data into the project, but clear it first
            IgpProject.Singleton.clearProject();
            IgpProject.Singleton.ReadData(openProjectDialog.FileName);
            IgpProject.Singleton.UpdateResolutions();
            IgpProject.Singleton.View.UpdateDistances();
            
            // Update UI
            SetWindowTitle(saveProjectDialog.FileName);
            EditorWindow.ReadViewProperities();
            PickerWindow.RefreshList();
            saveProjectDialog.FileName = openProjectDialog.FileName;
            EditorWindow.SetActivePage(0);

            // Render the view
            ClearView();
            Render();
            PaintView();
            LogMessage(saveProjectDialog.FileName + " successfully opened");

            // Update the list of recent docs
            UpdateDocumentsList(saveProjectDialog.FileName);
            RefreshDocumentsList();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveProjectDialog.FileName == "")
            {
                if (saveProjectDialog.ShowDialog() == DialogResult.OK)
                {
                    // Save data
                    project.SaveData(saveProjectDialog.FileName);

                    // Update UI
                    SetWindowTitle(saveProjectDialog.FileName);
                    
                    // Update the list of recent docs
                    UpdateDocumentsList(saveProjectDialog.FileName);
                    RefreshDocumentsList();

                    LogMessage(saveProjectDialog.FileName + " successfully saved");
                }
            }
            else
            {
                // Save data
                project.SaveData(saveProjectDialog.FileName);

                // Update the list of recent docs
                UpdateDocumentsList(saveProjectDialog.FileName);
                RefreshDocumentsList();
            }

        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveProjectDialog.ShowDialog() == DialogResult.OK)
            {
                // Save data
                project.SaveData(saveProjectDialog.FileName);

                // Update UI
                SetWindowTitle(saveProjectDialog.FileName);
                
                // Update the list of recent docs
                UpdateDocumentsList(saveProjectDialog.FileName);
                RefreshDocumentsList();

                LogMessage(saveProjectDialog.FileName + " successfully saved");
            }
        }

        private void saveBitmapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveBitmapDialog.ShowDialog() != DialogResult.OK) return;

            try
            {
                System.IO.FileStream fs = (System.IO.FileStream)saveBitmapDialog.OpenFile();

                switch (saveBitmapDialog.FilterIndex)
                {
                    case 1:
                        EncoderParameters encoderParameters = new EncoderParameters(1);
                        encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
                        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
                        ImageCodecInfo jpegCodec = null;

                        foreach (ImageCodecInfo imageCodecInfo in codecs)
                        {
                            if (jpegCodec == null && imageCodecInfo.MimeType == "image/jpeg")
                            {
                                jpegCodec = imageCodecInfo;
                            }
                        }
                        if (jpegCodec == null) throw new Exception("jpeg image codec not found");
                        IgpProject.Singleton.Bitmap.Save(fs, jpegCodec, encoderParameters);
                        break;

                    case 2:
                        IgpProject.Singleton.Bitmap.Save(fs, ImageFormat.Bmp);
                        break;

                    case 3:
                        IgpProject.Singleton.Bitmap.Save(fs, ImageFormat.Gif);
                        break;
                }

                fs.Close();

                LogMessage("Bitmap successfully saved");
            }
            catch (Exception ex)
            {
                LogMessage("error: "+ex.Message);
                throw;
            }
        }

        private void copyBitmapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(IgpProject.Singleton.Bitmap);
        }

        private void helpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Help.ShowHelp(this, "http://igp.googlecode.com/");
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutWindow.ShowDialog();
        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OptionsWindow.ShowDialog();
        }

        private void itemPickerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var menuItem = ((ToolStripMenuItem)sender);

            if (menuItem.Checked)
            {
                menuItem.Checked = false;
                PickerWindow.Hide();
            }
            else
            {
                menuItem.Checked = true;
                PickerWindow.Show();
            }

            igpSettings.Singleton.ShowPicker = menuItem.Checked;
        }

        private void itemEditorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var menuItem = ((ToolStripMenuItem)sender);

            if (menuItem.Checked)
            {
                menuItem.Checked = false;
                EditorWindow.Hide();
            }
            else
            {
                menuItem.Checked = true;
                EditorWindow.Show();
            }

            igpSettings.Singleton.ShowEditor = menuItem.Checked;
        }

        private void panelView_Paint(object sender, PaintEventArgs e)
        {
            Render();
            PaintView();
        }

        private void panelView_MouseMove(object sender, MouseEventArgs e)
        {
            //Transform Coordinates to View area

            e = new MouseEventArgs(e.Button, e.Clicks, e.X - DrawAtX, e.Y - DrawAtY, e.Delta);

            //Draw Interaction Results
            IgpProject.Singleton.View.UpdateDistances();
            Render();
            IgpProject.Singleton.View.InteractMove(IgpProject.Singleton.Graph, e);

            if (EditorWindow.GetActivePage() == 1)
            {
                IgpProject.Singleton.CurrentPointset.InteractMove(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 2)
            {
                IgpProject.Singleton.CurrentFunction.InteractMove(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 3)
            {
                IgpProject.Singleton.CurrentPolynomial.InteractMove(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 4)
            {
                IgpProject.Singleton.CurrentLabel.InteractMove(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 5)
            {
                IgpProject.Singleton.CurrentIntegral.InteractMove(IgpProject.Singleton.Graph, e);
            }

            PaintView();

            //Display Cursor Coordinates
            int Xc = e.X;// - DrawAtX;
            int Yc = e.Y;// - DrawAtY;
            
            if (Xc < 0 || Xc > IgpProject.Singleton.View.VWidth)
            {
                toolStripStatusLabelX.Text = "---";

            } else
            {
                toolStripStatusLabelX.Text = "x: " + IgpProject.Singleton.View.GetRealCoordinateX(Xc).ToString("0.000");
            }

            if (Yc < 0 || Yc > IgpProject.Singleton.View.VHeight)
            {
                toolStripStatusLabelY.Text = "---";
            } else
            {
                toolStripStatusLabelY.Text = "y: " + IgpProject.Singleton.View.GetRealCoordinateY(Yc).ToString("0.000");
            }
        }

        private void panelView_MouseDown(object sender, MouseEventArgs e)
        {
            e = new MouseEventArgs(e.Button, e.Clicks, e.X - DrawAtX, e.Y - DrawAtY, e.Delta);

            Render();
            IgpProject.Singleton.View.InteractDown(IgpProject.Singleton.Graph, e);


            if (EditorWindow.GetActivePage() == 1)
            {
                IgpProject.Singleton.CurrentPointset.InteractDown(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 2)
            {
                IgpProject.Singleton.CurrentFunction.InteractDown(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 3)
            {
                IgpProject.Singleton.CurrentPolynomial.InteractDown(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 4)
            {
                IgpProject.Singleton.CurrentLabel.InteractDown(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 5)
            {
                IgpProject.Singleton.CurrentIntegral.InteractDown(IgpProject.Singleton.Graph, e);
            }

            PaintView();
        }

        private void panelView_MouseUp(object sender, MouseEventArgs e)
        {
            e = new MouseEventArgs(e.Button, e.Clicks, e.X - DrawAtX, e.Y - DrawAtY, e.Delta);

            Render();
            IgpProject.Singleton.View.InteractUp(IgpProject.Singleton.Graph, e);

            if (EditorWindow.GetActivePage() == 1)
            {
                IgpProject.Singleton.CurrentPointset.InteractUp(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 2)
            {
                IgpProject.Singleton.CurrentFunction.InteractUp(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 3)
            {
                IgpProject.Singleton.CurrentPolynomial.InteractUp(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 4)
            {
                IgpProject.Singleton.CurrentLabel.InteractUp(IgpProject.Singleton.Graph, e);
            }

            if (EditorWindow.GetActivePage() == 5)
            {
                IgpProject.Singleton.CurrentIntegral.InteractUp(IgpProject.Singleton.Graph, e);
            }

            PaintView();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            IgpProject.Singleton.SaveSettings("igp.ini");
        }

        private void toolsToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            itemEditorToolStripMenuItem.Checked = EditorWindow.Visible;
            ItemPickerToolStripMenuItem.Checked = PickerWindow.Visible;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (IgpProject.Singleton.View.FitDraw)
            {
                FitViewToPanel();
            }

            ClearView();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void contextMenuStripRecentDocs_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            QuickOpenProject(e.ClickedItem.Text);
        }

        /// <summary>
        /// Loads project data from a specified file
        /// </summary>
        /// <param name="fileName">file path</param>
        public void QuickOpenProject(string fileName)
        {
            if (!System.IO.File.Exists(fileName))
            {
                RecentDocuments.Remove(fileName);
                LogMessage(string.Format("error: {0} not found", fileName));
                RefreshDocumentsList();
                return;
            }

            // Read data
            IgpProject.Singleton.clearProject();
            IgpProject.Singleton.ReadData(fileName);
            IgpProject.Singleton.UpdateResolutions();
            IgpProject.Singleton.View.UpdateDistances();

            // Update UI
            openProjectDialog.FileName = fileName;
            saveProjectDialog.FileName = fileName;
            EditorWindow.SetActivePage(0);
            SetWindowTitle(saveProjectDialog.FileName);
            EditorWindow.ReadViewProperities();
            PickerWindow.RefreshList();

            // Render the view
            ClearView();
            Render();
            PaintView();

            LogMessage(string.Format("{0} successfully opened", saveProjectDialog.FileName));
            
            // Update the list of recent docs
            UpdateDocumentsList(saveProjectDialog.FileName);
            RefreshDocumentsList();
        }

        private void importToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FormImport formImport = new FormImport();

            if (openProjectDialog.ShowDialog() == DialogResult.OK)
            {
                formImport.Display(openProjectDialog.FileName);
            }

        }

        private void FormMain_Move(object sender, EventArgs e)
        {
            if (formLog == null) return;
            
            formLog.Left = Left + toolStripStatusLabelMessage.Bounds.Left;
            formLog.Top = Top + Height - toolStripStatusLabelMessage.Bounds.Height - formLog.Height - 10;
        }

        private void toolStripStatusLabelMessage_Click(object sender, EventArgs e)
        {
            formLog.Update(log);
            formLog.Left = Left + toolStripStatusLabelMessage.Bounds.Left;
            formLog.Top = Top + Height - toolStripStatusLabelMessage.Bounds.Height - formLog.Height - 10;
            
            if (!formLog.Visible)
            {
                formLog.Show();
            }
            else
            {
                formLog.Hide();
            }
        }

        private void saveVisibleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveProjectDialog.ShowDialog() == DialogResult.OK)
            {
                // Save data
                project.SaveVisibleData(saveProjectDialog.FileName);

                // Update UI
                SetWindowTitle(saveProjectDialog.FileName);

                // Update the list of recent docs
                UpdateDocumentsList(saveProjectDialog.FileName);
                RefreshDocumentsList();

                LogMessage(string.Format("{0} successfully saved", saveProjectDialog.FileName));
            }
        }
    }
}